/*
 * Copyright (C) 2020 Baidu, Inc. All Rights Reserved.
 */
package completableFuture;

import com.sun.corba.se.spi.ior.iiop.IIOPFactories;
import org.springframework.util.StopWatch;
import sun.applet.Main;
import sun.jvm.hotspot.tools.SysPropsDumper;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Author wangtengyu
 * @Date 2020/8/24 3:32 下午
 * @Description
 */
public class CompleteableDemo2 {

    static {
        cachePool= Executors.newCachedThreadPool();
    }

    public static ExecutorService cachePool;

    public static void main(String args[]){
//        testRunAsync();
//        testSupplyAsyncTwo();
//        testSupplyAsyncException();
//        testSerial();
//        testSerialFuture();
//        testParallel();
//        testParallel2();
//        testGet();
        testWhenComplete();
    }


    /**
     * 用forkjoin.commonPool()
     */
    public static void testSupplyAsync()  {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(CompleteableDemo2::fetchPrice);
        cf.thenAccept(item->System.out.println(item));
        /*
            用默认的forkjoin的线程池，如果主线程结束了，那就不会继续执行，所以要等待
         */
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 用自己的线程池就不会有主线程结束，cf的线程也不执行的情况
     */
    public static CompletableFuture testSupplyAsyncTwo()  {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(CompleteableDemo2::fetchPrice,cachePool);
        cf.thenAccept(item->System.out.println(item));
        return cf;
    }

    /**
     * 用自己的线程池就不会有主线程结束，cf的线程也不执行的情况，一般来说都加一个异常的捕获
     */
    public static CompletableFuture testSupplyAsyncException()  {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(CompleteableDemo2::fetchPriceException,cachePool);
        cf.thenAccept(item->System.out.println(item));
        cf.exceptionally((e)->{
            System.out.println("出错了");
            e.printStackTrace();
            return null;
        });
        return cf;
    }


    /**
     * 串行执行，拿上一步异步的结果，作为下一步异步的条件(想比future，future需要get阻塞住当前线程
     * thenApply和thenApplyAsync区别，thenApply是用当前线程执行，thenApplyAsync是用forkjoin的commonPool或者指定线程池执行
     */
    public static void  testSerial(){
        CompletableFuture<String> cf=CompletableFuture.supplyAsync(()->getCityByName("张三"),cachePool);
        System.out.println("主线程没阻塞");
        CompletableFuture cf2=cf.thenApplyAsync(city->getProvinceByCity(city),cachePool);
        System.out.println("主线程没阻塞2");
        cf2.thenAcceptAsync(province->System.out.println(province),cachePool);

        //一条路走到底的写法，简洁但是可读性不是很强
//        CompletableFuture.supplyAsync(()->getCityByName("张三"),cachePool).thenApply(city->getProvinceByCity(city))
//                .thenAccept(province->System.out.println(province));
//        System.out.println("主线程没阻塞3");
    }

    /**
     * 如果是future版本的怎么写呢
     */
    public static void testSerialFuture(){
        try {
            System.out.println("主线程将阻塞xx秒");
            Future<String> future1=cachePool.submit(()->getCityByName("张三"));
            String city= future1.get();
            System.out.println("获取到city的值");
            String finalCity = city;
            Future<String> future2=cachePool.submit(()->getProvinceByCity(finalCity));
            System.out.println("主线程将阻塞xx秒");
            String province=future2.get();
            System.out.println("获取到province的值");
            System.out.println(province);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    /**
     * anyof
     * 并行执行，满足一个，谁快就用谁
     */
    public static void testParallel(){
        CompletableFuture<String> cf=CompletableFuture.supplyAsync(()->getCityByName("张三"),cachePool);
        CompletableFuture<String> cf2=CompletableFuture.supplyAsync(()->getCityByAddress("石林"),cachePool);
        CompletableFuture cfCombine=CompletableFuture.anyOf(cf,cf2);
        cfCombine.thenAccept(city->{
            String province=getProvinceByCity((String) city);
            System.out.println("获取到的城市是"+city);
            System.out.println("获取到的省份是"+province);
        });

    }

    /**
     * allof
     * 并行执行，满足所有
     */
    public static void testParallel2(){
        CompletableFuture<String> cf=CompletableFuture.supplyAsync(()->getCityByName("张三"),cachePool);
        CompletableFuture<String> cf2=CompletableFuture.supplyAsync(()->getCityByAddress("石林"),cachePool);
        CompletableFuture cfCombine=CompletableFuture.allOf(cf,cf2);
        cfCombine.thenAccept(city->{
            try {
                System.out.println(cf.get());
                System.out.println(cf2.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

    }


    /**
     * 不推荐使用这些操作
     * get 阻塞
     * get(x,x)超时
     * getNow()
     * join()和get相比，对抛出异常的处理不大一样
     */
    public static void testGet(){
        CompletableFuture<String> cf=CompletableFuture.supplyAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
//                int i=1/0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "done";
        });
        try {
            //get 阻塞
//            System.out.println(cf.get());
            //get 超时抛出一出场
//            System.out.println(cf.get(3, TimeUnit.SECONDS));

            //如果完成了，就返回完成的值（done),如果没完成，就返回给定的一个值。
//            TimeUnit.SECONDS.sleep(5);
//            System.out.println(cf.getNow("123"));

//             和get相比，抛出异常的细节不太一样
//            System.out.println(cf.join());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 测试一下whenComplete和thenApply、thenAccept的区别，暂时不大清楚，可能是whenComplete返回了异常
     *
     */
    public static void testWhenComplete() {
        CompletableFuture<String> cf=CompletableFuture.supplyAsync(()->getCityByName("张三"),cachePool);
        //模拟获取结果要获取5秒，这里等待2秒后直接complete一个值，complete()方法场景可以理解为超时的时候，给定一个值？
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (!cf.isDone()){
            cf.complete("这里是超时返回的值");
        }
        cf.whenComplete((city,e)->System.out.println(city));

    }








    static String getCityByName(String name){
        System.out.println("exec getCityByName");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        switch (name){
            case "张三":
                return "威海";
            case "李四":
                return "昆明";
            case "王五":
                return "烟台";
            default:
                return null;
        }
    }


    static String getCityByAddress(String address){
        System.out.println("exec getCityByAddress");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        switch (address){
            case "刘公岛":
                return "威海";
            case "石林":
                return "昆明";
            default:
                return null;
        }
    }

    static String getProvinceByCity(String city){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        switch (city){
            case "威海":
            case "烟台":
                return "山东";
            case "昆明":
                return "云南";
            default:
                return null;
        }
    }



    /**
     * 获取价格
     * @return
     */
    static Double fetchPrice() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 5 + Math.random() * 20;
    }

    /**
     * 获取价格抛出一次航
     * @return
     */
    static Double fetchPriceException() {
        throw new RuntimeException("fetch price error");
    }





}
